# Clase Lexer
#
# Proyecto Único - Versión 1.0
# Grupo R07:
# Cesar Hernández (06-39724)
# Daniel Valera (07-41725)

require "Token"

class Lexer
  attr_reader :input, :line, :column
  attr_writer :line, :column
  
  def initialize(input)
	@input = input
	
	if @input.kind_of? String
		@buffer = @input.dup
	else
		@buffer = @input.gets
	end
	
    @line   = 1
    @column = 1
  end
  
  def skip(i=1)
    @buffer = @buffer[i .. -1]
    @column = @column + i
  end
  
  def newline()
    @buffer = @input.gets
    @line   = @line + 1
    @column = 1
  end
  
  def yylex()
    auxline   = @line
    auxcolumn = @column
    
    while true
      case @buffer
      
		#Espacios en blanco y tabuladores
        when /\A( |\t)+/ 
          skip($&.length)
          auxcolumn = @column
        
        #Saltos de línea
        when /\A(\n|\\n)/
			begin
				if @input.kind_of? String
					@buffer = @buffer[2 .. -1]
					@line += 1
					auxline = @line
					@column = 1
					auxcolumn = @column
				else
					newline()
					auxline = @line
					auxcolumn = @column
				end
			end
        
        # Comentarios que terminan en salto de línea
        when /\A\#.*?(\n|\\n)/
			begin
				if @input.kind_of? String
					@buffer = @buffer[$&.length .. -1]
					@column = 1
					auxcolumn = @column
					
					@line += 1
					auxline = @line
				else
					newline()
					auxline = @line
					auxcolumn = @column
				end
			end
			
		# Comentarios que no terminan en salto de línea
		when /\A(\#.*)/
			begin
				skip($&.length)
				auxcolumn = @column
			end
		
        #Números
        when /\A(\-)?(\d*)(\.)(\d+)/
          begin
            skip($&.length)
            return TkNum.new(auxline, auxcolumn, $&.to_f)
          end
        when /\A(\-)?(\d+)(\.*)/
          begin
            skip($&.length)
            return TkNum.new(auxline, auxcolumn, $&.to_i)
          end
        
        #Operadores
        when /\A\+/
          begin
            skip(1)
            return TkPlus.new(auxline, auxcolumn)
          end
        when /\A\-/
          begin
            skip(1)
            return TkMinus.new(auxline, auxcolumn)
          end
        when /\A\*/
          begin
            skip(1)
            return TkMult.new(auxline, auxcolumn)
          end
        when /\A\//
          begin
            skip(1)
            return TkDiv.new(auxline, auxcolumn)
          end
        when /\A\%/
          begin
            skip(1)
            return TkMod.new(auxline, auxcolumn)
          end
        when /\A\*\*/
          begin
            skip(2)
            return TkPower.new(auxline, auxcolumn)
          end
        when /\A\</
          begin
            skip(1)
            return TkLess.new(auxline, auxcolumn)
          end
        when /\A\>/
          begin
            skip(1)
            return TkGreater.new(auxline, auxcolumn)
          end
        when /\A\<\=/
          begin
            skip(2)
            return TkLessEqual.new(auxline, auxcolumn)
          end
        when /\A\>\=/
          begin
            skip(2)
            return TkGreaterEqual.new(auxline, auxcolumn)
          end
        when /\A=/
          begin
            skip(1)
            return TkEqual.new(auxline, auxcolumn)
          end
        when /\A\!\=/
          begin
            skip(2)
            return TkNotEqual.new(auxline, auxcolumn)
          end
        when /\A\$/
          begin
            skip(1)
            return TkDollar.new(auxline, auxcolumn)
          end
        when /\A\@/
          begin
            skip(1)
            return TkAt.new(auxline, auxcolumn)
          end
        when /\A\’/
          begin
            skip(1)
            return TkPrime.new(auxline, auxcolumn)
          end
        when /\A\&\&/
          begin
            skip(2)
            return TkAnd.new(auxline, auxcolumn)
          end
        when /\A\|\|/
          begin
            skip(2)
            return TkOr.new(auxline, auxcolumn)
          end
        when /\A\!/
          begin
            skip(2)
            return TkNot.new(auxline, auxcolumn)
          end
        when /\A\:\=/
          begin
            skip(2)
            return TkAssign.new(auxline, auxcolumn)
          end
        
        #Signos de puntuación y separadores
        when /\A\./
          begin
            skip(1)
            return TkDot.new(auxline, auxcolumn)
          end
        when /\A\,/
          begin
            skip(1)
            return TkComma.new(auxline, auxcolumn)
          end
        when /\A\:/
          begin
            skip(1)
            return TkColon.new(auxline, auxcolumn)
          end
        when /\A\;/
          begin
            skip(1)
            return TkSemiColon.new(auxline, auxcolumn)
          end
        when /\A\(/
          begin
            skip(1)
            return TkOpParen.new(auxline, auxcolumn)
          end
        when /\A\)/
          begin
            skip(1)
            return TkClParen.new(auxline, auxcolumn)
          end
        when /\A\[/
          begin
            skip(1)
            return TkOpBrackets.new(auxline, auxcolumn)
          end
        when /\A\]/
          begin
            skip(1)
            return TkClBrackets.new(auxline, auxcolumn)
          end
        when /\A\{/
          begin
            skip(1)
            return TkOpBraces.new(auxline, auxcolumn)
          end
        when /\A\}/
          begin
            skip(1)
            return TkClBraces.new(auxline, auxcolumn)
          end
          
        #Texto
        when /\A(\"(\\\"|[^\"])*\"|\'(\\\'|[^\'])*\')/
          begin
            skip($&.length)
            return TkString.new(auxline, auxcolumn, $&)
          end
        
        #Palabras
        when /\A(\w+)/
          begin
            skip($&.length)
            
            case $&
              #Declaraciones
			  when "num"
                return TkIsNum.new(auxline, auxcolumn)
              when "vec"
                return TkIsVec.new(auxline, auxcolumn)
              when "mat"
                return TkIsMat.new(auxline, auxcolumn)
              when "String"
                return TkIsString.new(auxline, auxcolumn)
              when "Booleano"
                return TkIsBool.new(auxline, auxcolumn)
              when "vars"
                return TkIsVar.new(auxline, auxcolumn, $&)
              
              #True y false
              when "true"
                return TkBool.new(auxline, auxcolumn, $&)
              when "false"
                return TkBool.new(auxline, auxcolumn, $&)
              
              #Funciones
              when "zeroes"
                return TkZeroes.new(auxline, auxcolumn)
              when "range"
                return TkRange.new(auxline, auxcolumn)
              when "eye"
                return TkEye.new(auxline, auxcolumn)
              when "define"
                return TkDefine.new(auxline, auxcolumn)
              when "of"
                return TkOf.new(auxline, auxcolumn)
              when "type"
                return TkType.new(auxline, auxcolumn)
              when "as"
                return TkAs.new(auxline, auxcolumn)
              when "return"
                return TkReturn.new(auxline, auxcolumn)
              
              #Instrucciones
				#Bloques de instrucciones
              when "begin"
                return TkBegin.new(auxline, auxcolumn)
              when "end"
                return TkEnd.new(auxline, auxcolumn)
                #Selección condicional
              when "if"
                return TkIf.new(auxline, auxcolumn)
              when "then"
                return TkThen.new(auxline, auxcolumn)
              when "else"
                return TkElse.new(auxline, auxcolumn)
                #Iteración condicional
              when "while"
                return TkWhile.new(auxline, auxcolumn)
              when "do"
                return TkDo.new(auxline, auxcolumn)
                #Iteración sobre un vector/matriz
              when "foreach"
                return TkForEach.new(auxline, auxcolumn)
              when "in"
                return TkIn.new(auxline, auxcolumn)
                #Lectura y escritura
              when "read"
                return TkRead.new(auxline, auxcolumn)
              when "write"
                return TkWrite.new(auxline, auxcolumn)
              
              #Identificadores
              else
                return TkId.new(auxline, auxcolumn, $&)
            end
          end
        else
			if @input.kind_of? String
				if @buffer.empty?
					return nil
				else
					error = @buffer[0,1]
					skip(1)
					raise "Error: Carácter no esperado #{error} en línea #{auxline}, columna #{auxcolumn}."
				end
			else
				if @input.eof? then
					return nil
				else
					error = @buffer[0,1]
					skip(1)
					raise "Error: Carácter no esperado #{error} en línea #{auxline}, columna #{auxcolumn}."
				end
			end
		end
      end
    end
   
end
